package stream;



import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * stream API/流式计算
 */
public class TestStream {

    public static void main(String[] args) {
        /**============================常用操作===============================*/
        //创建Person
        Person person1 = new Person(1, "a", 22);
        Person person2 = new Person(2, "a", 34);
        Person person3 = new Person(3, "c", 10);
        Person person4 = new Person(4, "d", 110);
        Person person5 = new Person(5, "e", 9);
        Person person6 = new Person(6, "f", 18);

        //放入集合，流式计算主要针对集合
        List<Person> people = Arrays.asList(person1, person2, person3, person4, person5, person6);
        System.out.println("list=>" + people);

        /*
            1.people.stream()：把people转成流对象，目的是为了使用流的各个方法，就可以处理一些复杂的操作
            2.filter：传入的是 Predicate<? super T> predicate 断言，返回boolean
            3.collect：传入的是 Collector，将数据收集到集合中
            4.map：希望给过滤得到的person对象 加入 cat对象。用于一对一的转换，生成的流与原始流结构相同。
            5.sorted：排序，传入的是Comparator比较器
            6.limit：取出指定个数据
            7.count：统计集合数据量
            8.flatMap：不仅转换每个元素，还将多个流合并为一个流（即将嵌套结构打平）
         */
        List<Person> filterResult = people.stream().filter(e -> {
                return e.getId() % 2 != 0;
        }).map(person -> {
            Cat cat = new Cat(person.getId() + 100, "小花猫", "花色");
            person.setCat(cat);
            return person;
        }).sorted((p1, p2) -> { //按照id降序排列，函数式写法，实际是传入了实现Comparator的匿名内部类
            return p2.getId() - p1.getId();
        }).limit(2).collect(Collectors.toList());
        System.out.println("filterResult=>" + filterResult);
        long count = filterResult.stream().count();
        System.out.println("count=>" + count);
        //如果只有filter操作，people本身没有变化
        //当执行了map操作，people本身也会执行map操作
        System.out.println("list=>" + people);

        /**======================flatMap使用=============================*/
        //使用flatMap将每个字符串转换为字符流并打平
        /*
            通过flatMap()，将每个字符串转换为字符流，接着将这些字符流合并为一个单一的流
            最终的结果是一个包含所有字符的List<String>，而不是嵌套的List<String[]>
         */
        List<String> words = Arrays.asList("Hello", "World");
        List<String> result = words.stream().flatMap(word -> Arrays.stream(word.split("")))
                .collect(Collectors.toList());
        System.out.println("result=>" + result);    //[H,e,l,l,o,W,o,r,l,d]

        List<String> approvalObjectList = Arrays.asList("1,2", "3,4");
        List<String> collect = approvalObjectList.stream()
                .flatMap(obj -> Arrays.stream(obj.split(",")))
                .collect(Collectors.toList());
        System.out.println("collect=>" + collect);  //[1,2,3,4]

        //场景一：打平嵌套的集合
        List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b", "c"),
                Arrays.asList("d", "e"),
                Arrays.asList("f", "g", "h"));
        List<String> flattenedList = listOfLists.stream()
                .flatMap(list -> list.stream()) //将每个列表转换为流，并扁平化
                .collect(Collectors.toList());
        System.out.println("flattenedList=>" + flattenedList);

        //场景二：处理嵌套的数据结构
        person1.setHobbies(Arrays.asList("Reading", "Writing"));
        person2.setHobbies(Arrays.asList("Cycling", "Hiking"));
        person3.setHobbies(Arrays.asList("Swimming", "Playing"));
        person4.setHobbies(Arrays.asList("Eating", "Drinking"));
        person5.setHobbies(Arrays.asList("Laughing", "Crying"));
        person6.setHobbies(Arrays.asList("Hugging", "Kissing"));
        List<String> allHobbies = people.stream().flatMap(person -> person.getHobbies().stream())
                .collect(Collectors.toList());
        System.out.println("allHobbies=>" + allHobbies);

        /**=========================聚合操作===============================*/
        /*
            1.anyMatch(Predicate<T>)：是否有元素满足条件
            2.allMatch(Predicate<T>)：是否所有元素都满足条件
            3.noneMatch(Predicate<T>)：是否没有元素满足条件
            4.findFirst()：返回第一个元素（Optional）
            5.findAny()：返回任意元素（Optional，并行流中更高效）
            6.max(Comparator<T>) 和 min(Comparator<T>)：返回最大 / 小值
         */
        boolean anyMatch = people.stream().anyMatch(p -> p.getHobbies().contains("Hiking"));
        System.out.println("anyMatch=>" + anyMatch);    //true
        boolean allMatch = people.stream().allMatch(p -> null != p.getCat());
        System.out.println("allMatch=>" + allMatch);    //false
        boolean noneMatch = people.stream().noneMatch(p -> p.getHobbies().contains("Hiking"));
        System.out.println("noneMatch=>" + noneMatch);  //false
        String first = Arrays.asList("10", "2", "3", "4", "5", "6", "7", "8", "9").stream().findFirst().get();
        System.out.println("first=>" + first);  //Optional[10]
        String any = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9").stream().findAny().get();
        System.out.println("any=>" + any);  //Optional[1]
        Integer max = Arrays.asList(1, 2, 80, 101, 13, 25, 90).stream().max(Integer::compare).get();
        System.out.println("max=>" + max);

        /**=========================收集操作===============================*/
        //转换为list
        List<Person> toList = people.stream().collect(Collectors.toList());
        //转换为特定类型的集合
        LinkedList<Person> linkedList = people.stream().collect(Collectors.toCollection(LinkedList::new));

        //转换为map
        Map<Integer, Person> map = people.stream().collect(Collectors.toMap(Person::getId, p -> p));
        System.out.println("map=>" + map);
        //处理重复键(保留最新值)
        Map<String, Person> distinctMap = people.stream().
                collect(Collectors.toMap(Person::getName, Function.identity(),(existing, replacement) -> replacement));
        System.out.println("distinctMap=>" + distinctMap);

        //分组
        Map<String, List<Person>> groupMap = people.stream().collect(Collectors.groupingBy(Person::getName));
        System.out.println("groupMap=>" + groupMap);

        //按条件分区
        /*
            partitioningBy 会根据给定的 predicate 将流中的元素分为两个集合：
            满足 predicate 条件的元素会被放到 Map 中键为 true 的列表中。
            不满足 predicate 条件的元素会被放到 Map 中键为 false 的列表中。
         */
        Map<Boolean, List<Person>> partMap = people.stream().collect(Collectors.partitioningBy(p -> p.getId() % 2 == 0));
        System.out.println("partMap=>" + partMap);

        //计算总和
        Integer ageSum = people.stream().collect(Collectors.summingInt(Person::getAge));
        System.out.println("ageSum=>" + ageSum);

        //计算平均值
        Double ageAverage = people.stream().collect(Collectors.averagingInt(Person::getAge));
        System.out.println("ageAverage=>" + ageAverage);

        //字符串拼接
        String joined = Arrays.asList("a", "b", "c").stream()
                .collect(Collectors.joining(",", "(", ")"));
        System.out.println("joined=>" + joined);    //(a,b,c)

    }
}

//Person类
class Person {

    private Integer id;

    private String name;

    private Integer age;

    private Cat cat;

    private List<String> hobbies;

    public Person(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Person(Integer id, String name, Integer age, List<String> hobbies) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", cat=" + cat +
                ", hobbies=" + hobbies +
                '}';
    }
}

//猫类
class Cat {

    private Integer id;

    private String name;

    private String color;

    public Cat(Integer id, String name, String color) {
        this.id = id;
        this.name = name;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}